#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/workqueue.h>


/**
 * ft5x06硬件i2c 完整代码
 * 
 adb:      insmod  ft5x06_driver_dts_driver.ko    
 串口日志：
[  747.253673] input: ft5x06_dev as /devices/virtual/input/input3
[  747.254120] this is  ft5x06_probe 


/dev/input/event3  
        rk3568_r:/ # cat /sys/class/input/event3/device/name
        ft5x06_dev
 */

// 声明工作队列要用到的函数
void ft5x06_func(struct work_struct *work);

// 声明一个工作结构体
static DECLARE_WORK(ft5x06_work, ft5x06_func);

// 定义 ft5x06 设备的 GPIO 描述符
// 用于保存 reset 和 interrupt 引脚的 GPIO 描述符
struct gpio_desc *reset_gpio, *irq_gpio;

// 保存 ft5x06 设备的 i2c 客户端对象指针
struct i2c_client *ft5x06_client;

// 保存 ft5x06 输入设备的dev指针
struct input_dev *ft5x06_input_dev;

// 读取 ft5x06 设备寄存器的函数
int ft5x06_read_reg(u8 reg_addr)
{
    u8 data;
    // 定义两个 i2c_msg 结构体,分别表示写操作和读操作， 读写数据包结构体i2c_msg
    struct i2c_msg msgs[2] = {
        [0] = {
            .addr = ft5x06_client->addr, // 设备地址
            .flags = 0,        // 写操作，.flags=0  
            .len = sizeof(reg_addr),
            .buf = &reg_addr,  // 写入要读取的寄存器地址
        },
        [1] = {
            .addr = ft5x06_client->addr,
            .flags = I2C_M_RD, // 读操作， .flags=1 
            .len = sizeof(data),
            .buf = &data,      // 读取到的数据存储位置
        },
    };

   // 使用 i2c_transfer 函数进行 i2c 总线读取操作
   // 如果读取失败,返回 -EIO 错误码
   if (i2c_transfer(ft5x06_client->adapter, msgs, ARRAY_SIZE(msgs)) != ARRAY_SIZE(msgs)){
        return -EIO;
    }

    return data; // 返回读取到的寄存器值
}

// 向 ft5x06 设备写入寄存器的函数
void ft5x06_write_reg(u8 reg_addr, u8 *data, u16 len)
{
    u8 buff[256];
    struct i2c_msg msgs[] = {
        [0] = {
            .addr = ft5x06_client->addr, // 设备地址
            .flags = 0,        // 写操作
            .len = len + 1,    // 数据长度 + 寄存器地址长度, 下面buf[0]把地址页加入了
            .buf = buff,       // 数据缓冲区
        },
    };

    buff[0] = reg_addr;       // 写入寄存器地址
    memcpy(&buff[1], data, len); // 写入数据

    // 使用 i2c_transfer 函数进行 i2c 总线写入操作
    // 如果写入失败,直接返回
    if (i2c_transfer(ft5x06_client->adapter, msgs, ARRAY_SIZE(msgs)) != ARRAY_SIZE(msgs)) {
        return;
    }
}


// 工作函数, 中断下文
void ft5x06_func(struct work_struct *work)
{
    int TOUCH1_XH, TOUCH1_XL, x;
    int TOUCH1_YH, TOUCH1_YL, y;
    int TD_STATUS;

    // 从寄存器中读取触摸坐标数据
    /*
低字节[7:0]  表示坐标
高字节[3:0] 表示坐标
X轴坐标12位构成， 
    */
    TOUCH1_XH = ft5x06_read_reg(0x03);
    TOUCH1_XL = ft5x06_read_reg(0x04);
    x = ((TOUCH1_XH << 8) | TOUCH1_XL) & 0xfff;

    TOUCH1_YH = ft5x06_read_reg(0x05);
    TOUCH1_YL = ft5x06_read_reg(0x06);
    y = ((TOUCH1_YH << 8) | TOUCH1_YL) & 0xfff;

    // 读取触摸屏状态寄存器0x02  
    // bit[3:0]   返回1单点，2点，5点触摸
    TD_STATUS = ft5x06_read_reg(0x02);
    TD_STATUS = TD_STATUS & 0xf;

    // 根据触摸状态更新输入设备
    if (TD_STATUS == 0) {
        // 触摸释放
        input_report_key(ft5x06_input_dev, BTN_TOUCH, 0);
        input_sync(ft5x06_input_dev);
    } else {
        // 触摸按下
        input_report_key(ft5x06_input_dev, BTN_TOUCH, 1);
		//上报坐标
        input_report_abs(ft5x06_input_dev, ABS_X, x);
        input_report_abs(ft5x06_input_dev, ABS_Y, y);
        input_sync(ft5x06_input_dev);
    }
}

// ft5x06 中断处理函数
irqreturn_t ft5x06_handler(int irq, void *args)
{
    // 将 ft5x06_work 工作结构体提交到工作队列中
    // 调度工作队列
    schedule_work(&ft5x06_work);

    // 表示中断已经被处理
    return IRQ_RETVAL(IRQ_HANDLED);
}

// ft5x06 设备的初始化函数
int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret;
	int value =0; 
    printk("This is ft5x06 probe\n");
    ft5x06_client = client; // 保存 i2c 客户端对象指针

    // 获取 reset GPIO 描述符
    // gpiod_get_optional 函数尝试获取设备树中名为 "reset" 的 GPIO 描述符,
    // 如果获取失败则返回 NULL
    reset_gpio = gpiod_get_optional(&client->dev, "reset", 0);
    if (!reset_gpio) {
        printk("gpiod_get_optional reset gpio is error\n");
        return -1;
    }

    // 获取 irq GPIO 描述符
    // gpiod_get_optional 函数尝试获取设备树中名为 "interrupts" 的 GPIO 描述符,
    // 如果获取失败则返回 NULL
    irq_gpio = gpiod_get_optional(&client->dev, "interrupts", 0);
    if (!irq_gpio) {
        printk("gpiod_get_optional irq gpio is error\n");
        return -1;
    }

    // 拉低，延迟，拉高，完成复位操作
    // 设置 reset GPIO 为输出,并拉低 5ms 后拉高
    // 这是一个复位操作,用于初始化 ft5x06 设备
    gpiod_direction_output(reset_gpio, 0);
    msleep(5);
    gpiod_direction_output(reset_gpio, 1);

    // 请求中断,设置为下降沿触发,单次触发
    // 当 ft5x06 设备产生中断时,会调用 ft5x06_handler 函数处理
    // 可以直接获取中断号：client->irq
    ret = request_irq(client->irq, ft5x06_handler,
                     IRQ_TYPE_EDGE_FALLING | IRQF_ONESHOT, "ft5x06 irq", NULL);
    if (ret < 0) {
        printk("request irq is error\n");
        return -2;
    }

	/**
	 * 17.编写FT5X06驱动程序的I2C通信部分（代码编写） 
	 *    测试:ft5x06_read_reg 和 ft5x06_write_reg 函数
	 *         测试寄存器0x80写入0x4b，写入1个字节
	 *              然后在用i2c读出来，看i2c驱动是否成功，读出来还是0x4b
	 */ 
#if 0
	 ft5x06_write_reg(0x80,0x4b,1);
	 value = ft5x06_read_reg(0x80);
	 printk("reg 0x80 is %x \n",val);
#endif 



#if 1
    // 分配一个 input 设备
    ft5x06_input_dev = input_allocate_device();
    if (ft5x06_input_dev == NULL) {
        printk("input allocate device is error\n");
        return -2;
    }

    // 设置 input 设备的名称
    ft5x06_input_dev->name = "ft5x06_dev";

    // 设置 input 设备支持的事件类型和事件
    set_bit(EV_KEY, ft5x06_input_dev->evbit);  //按键事件
    set_bit(BTN_TOUCH, ft5x06_input_dev->keybit);  //按下事件
//上面2个一对

    set_bit(EV_ABS, ft5x06_input_dev->evbit);  //绝对事件，下面是具体事件
    set_bit(ABS_X, ft5x06_input_dev->absbit);
    set_bit(ABS_Y, ft5x06_input_dev->absbit);
//上面3个一对

    // 设置 input 设备的绝对坐标范围  7寸mipi 分辨率1280*800
    input_set_abs_params(ft5x06_input_dev, ABS_X, 0, 800, 0, 0);
    input_set_abs_params(ft5x06_input_dev, ABS_Y, 0, 1280, 0, 0);

    // 注册 input 设备
    ret = input_register_device(ft5x06_input_dev);
    if (ret < 0) {
        printk("input register device is error\n");
        goto error_0;
    }

    return 0;

error_0:
    input_free_device(ft5x06_input_dev);
    free_irq(client->irq, NULL);
    gpiod_put(reset_gpio);
    gpiod_put(irq_gpio);

#endif 


    return ret;
}

// ft5x06 设备的移除函数
// 参数 client 是 i2c 客户端对象指针
int ft5x06_remove(struct i2c_client *client)
{
    // 释放中断
    free_irq(client->irq, NULL);
    // 释放 GPIO 资源
    gpiod_put(reset_gpio);
    gpiod_put(irq_gpio);
    return 0;
}

// 定义 i2c_device_id 结构体数组,用于标识 ft5x06 设备
static const struct i2c_device_id ft5x06_id[] = {
    { "my-ft5x06", 0 },
    { }
};

// 定义 i2c_driver 结构体,描述 ft5x06 设备驱动
static struct i2c_driver ft5x06_driver = {
    .driver = {
        .name = "my-ft5x06",
        .owner = THIS_MODULE,
    },
    .probe = ft5x06_probe,
    .remove = ft5x06_remove,
    .id_table = ft5x06_id,
};

// 驱动初始化函数
static int __init ft5x06_driver_init(void)
{
    int ret;
    // 注册 I2C 设备驱动
    ret = i2c_add_driver(&ft5x06_driver);
    if (ret < 0) {
        printk("i2c_add_driver is error\n");
        return ret;
    }
    return 0;
}

// 驱动退出函数
static void ft5x06_driver_exit(void)
{
    /* 释放中断 */
    free_irq(ft5x06_client->irq, NULL);

    /* 注销输入设备 */
    input_unregister_device(ft5x06_input_dev);

    /* 释放 GPIO */
    gpiod_put(reset_gpio);
    gpiod_put(irq_gpio);

    /* 删除 I2C 驱动 */
    i2c_del_driver(&ft5x06_driver);
}

module_init(ft5x06_driver_init);
module_exit(ft5x06_driver_exit);
MODULE_LICENSE("GPL");
